﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;


namespace Rogue
{
    public class Tutorial
    {
        public Tutorial()
        {
            m_Title = "";
            m_WholeMsg = "";
            m_Lines = new List<string>();
            m_Position = TutWindow.Instance.OrigPosition;
            m_Read = false;

            if (Timeline.Instance.LastGameTime != null)
                m_TimeAtShown = Timeline.Instance.LastGameTime.TotalGameTime.TotalMilliseconds;
            else
                m_TimeAtShown = 0;

            m_MinTimeToShow = 0;

            m_TargetEntity = null;
            m_TargetWindow = null;
        }

        public double GetAge()
        {
            return (Timeline.Instance.LastGameTime.TotalGameTime.TotalMilliseconds - m_TimeAtShown) / 1000.0f;
        }

        public string m_Title;

        public string m_WholeMsg;
        public List<string> m_Lines;

        public Point m_Position;

        public bool m_Read;
        public double m_TimeAtShown;

        public double m_MinTimeToShow;

        public Entity m_TargetEntity;
        public CellWindow m_TargetWindow;
    };

    public class TutWindow : TextWindow
    {
        static TutWindow m_Instance = null;
        static public TutWindow Instance
        {
            get { return m_Instance; }
            set { m_Instance = value; }
        }


        protected bool m_bDismissed = false;        // user presses T, never wants to see a tutorial
        protected bool m_bVisible = false;
        public bool IsActive
        { get { return m_bVisible && !m_bDismissed; } }

        
        List<Tutorial> m_Tutorials;
        List<Tutorial> m_DeferredTuts;      // waiting to show

        int m_TutIndexToShow;



        public TutWindow(
            Texture2D a_Tex,
            GraphicsDevice a_Dev,
            Point a_pPos,
            Point a_pCellMapSize,
            Point a_pCellScreenSize,
            Point a_pCharacterOffset)
            : base(a_Tex, a_Dev, a_pPos, a_pCellMapSize, a_pCellScreenSize, a_pCharacterOffset)
        {
            
            DrawBackground = true;
            BackgroundColor = new Color(20, 20, 20);

            BorderColor = new Color(160, 160, 60);
            BorderBottom = BorderTop = BorderLeft = BorderRight = true;

            m_OrigHeight = Height;
            m_OrigPosition = Position;

            m_Tutorials = new List<Tutorial>();
            m_DeferredTuts = new List<Tutorial>();

            InputManager.Instance.OnKeyPressed += new InputManager.OnKeyPressedHandler(Instance_OnKeyPressed);
        }

        public bool HasUnreadTuts()
        {
            foreach (Tutorial t in m_Tutorials)
                if (!t.m_Read)
                    return true;

            return false;
        }

        void Instance_OnKeyPressed(object a_Sender, InputManager.KeyEventArgs a_KeyEventArgs)
        {
            if (a_KeyEventArgs.Key == Keys.T)
            {
                if (IsActive)
                {
                    // active, going dismissed
                    Close();

                    m_bDismissed = true;   
                }
                else
                {
                    // dismissed or not active, going visible
                    m_bVisible = true;
                    m_bDismissed = false;
                    m_bKeepTutVisible = true;

                    if (GetActiveTut() != null)
                    {
                        if (Timeline.Instance.LastGameTime != null)
                            GetActiveTut().m_TimeAtShown = Timeline.Instance.LastGameTime.TotalGameTime.TotalMilliseconds;
                        else
                            GetActiveTut().m_TimeAtShown = 0;
                    }

                    int count = 0;
                    foreach (Tutorial t in m_Tutorials)
                    {
                        if (!t.m_Read)
                        {
                            m_TutIndexToShow = count;
                        }
                        count++;
                    }
                }
            }

            if (IsActive)
            {
                if (a_KeyEventArgs.Key == Keys.OemComma)
                {
                    --m_TutIndexToShow;
                }

                if (a_KeyEventArgs.Key == Keys.OemPeriod)
                {
                    ++m_TutIndexToShow;
                }

                if (a_KeyEventArgs.Key == Keys.X)
                {
                    Close();
                }

                m_TutIndexToShow = Math.Max(0, Math.Min(m_Tutorials.Count - 1, m_TutIndexToShow));
            }
        }

        public Tutorial GetActiveTut()
        {
            if (m_Tutorials.Count == 0)
                return null;

            return m_Tutorials[m_TutIndexToShow];
        }

        bool m_bHasSetMoveCallback = false;
        bool m_bKeepTutVisible = false;

        public override void Update(GameTime dt)
        {
            Entity hero = RogueGame.Instance.Map.GetHero();

            if (!m_bHasSetMoveCallback)
            {
                hero.OnMove += new Entity.MoveHandler(hero_OnMove);
            }

            Tutorial t = GetActiveTut();

            if (m_DeferredTuts.Count > 0)
            {
                bool shortWaitAndMove = t.m_MinTimeToShow < t.GetAge() && !m_bKeepTutVisible;
                if (t == null || (!IsActive || shortWaitAndMove) )
                {
                    m_bVisible = true;
                    m_bKeepTutVisible = true;

                    Tutorial deferredT = m_DeferredTuts[0];
                    m_DeferredTuts.RemoveAt(0);
                    m_Tutorials.Add(deferredT);                    
                    deferredT.m_TimeAtShown = Timeline.Instance.LastGameTime.TotalGameTime.TotalMilliseconds;
                    m_TutIndexToShow = m_Tutorials.Count - 1; 
                }
            }

            // the tutorial's been up a long time, and we're moving around. Hide it
            if (t != null && t.m_MinTimeToShow + 10.0f < t.GetAge() && !m_bKeepTutVisible && IsActive)
            {
                Close();
            }
        }

        void hero_OnMove(Entity.MoveArgs e)
        {
            Tutorial t = GetActiveTut();
            if (t != null && t.m_MinTimeToShow < t.GetAge())
            {
                m_bKeepTutVisible = false;
            }
        }

        Point m_OrigPosition;
        
        public Point OrigPosition
        { get { return m_OrigPosition; } }

        int m_OrigHeight;

        ButtonState m_LastLMBState = ButtonState.Released;

        public override void Draw(GraphicsDevice a_Device)
        {
            if (!IsActive)
            {
                return;
            }

            Tutorial t = GetActiveTut();

            t.m_Read = true;

            if (Position != t.m_Position)
            {
                base.Position = t.m_Position;
            }

            // resize the window if we need to
            int newDesiredHeight = Math.Max(5, 2 + t.m_Lines.Count);
            if (newDesiredHeight != Height)
            {
                Reset(m_aCellMap.GetLength(0), newDesiredHeight, m_pCellScreenSize);
            }

            string title = t.m_Title.ToUpper();

            if (m_TutIndexToShow != m_Tutorials.Count - 1)
            {
                title += string.Format(" ({0}/{1})", m_TutIndexToShow + 1, m_Tutorials.Count);
            }
            PutString(title, 2, 0, Color.Yellow);

            // show the next/prev buttons
            if (m_Tutorials.Count > 1)
            {
                bool canGoLeft = m_TutIndexToShow > 0;
                bool canGoRight = m_TutIndexToShow < m_Tutorials.Count - 1;

                PutChar('<', canGoLeft ? Color.Yellow : Color.Gray, (char)255, Color.Black, new Point(Width - 10, 0));
                PutChar('>', canGoRight ? Color.Yellow : Color.Gray, (char)255, Color.Black, new Point(Width - 8, 0));
                PutChar('x', Color.Yellow, (char)255, Color.Black, new Point(Width - 2, 0));

                MouseState mouseState = Mouse.GetState();
                Point clickPos = new Point(mouseState.X, mouseState.Y);

                if (mouseState.LeftButton == ButtonState.Pressed && m_LastLMBState == ButtonState.Released)
                {
                    if (GetScreenCellBounds(Width - 10, 0).Contains(clickPos))
                    {
                        --m_TutIndexToShow;
                    }
                    else if (GetScreenCellBounds(Width - 8, 0).Contains(clickPos))
                    {
                        ++m_TutIndexToShow;
                    }
                    else if (GetScreenCellBounds(Width - 2, 0).Contains(clickPos))
                    {
                        Close();
                    }

                    m_TutIndexToShow = Math.Max(0, Math.Min(m_Tutorials.Count-1, m_TutIndexToShow));
                }

                m_LastLMBState = mouseState.LeftButton;
            }

            int numLines = 2;
            foreach (string s in t.m_Lines)
            {
                PutString(s, 1, numLines);
                ++numLines;
            }

            if (t.m_TargetEntity != null || t.m_TargetWindow != null)
            {
                Rectangle targetRect = new Rectangle();
                if (t.m_TargetEntity != null)
                {
                    Point windowSpace = RogueGame.Instance.Map.Camera.CellToWindowSpace(t.m_TargetEntity.Position);
                    targetRect = TerrainWindow.Instance.GetScreenCellBounds(windowSpace);
                }
                else if (t.m_TargetWindow != null)
                {
                    targetRect = t.m_TargetWindow.WindowBounds;
                }

                TerrainWindow.Instance.DrawRectOutline(targetRect, BorderColor);

                Point middleOfTutWindow = new Point(WindowBounds.Left + WindowBounds.Width / 2, WindowBounds.Top + WindowBounds.Height / 2);

                Point onBorderOfTarget = middleOfTutWindow;
                onBorderOfTarget.X = Math.Max(targetRect.Left, Math.Min(targetRect.Right, onBorderOfTarget.X));
                onBorderOfTarget.Y = Math.Max(targetRect.Top, Math.Min(targetRect.Bottom, onBorderOfTarget.Y));

                Point onBorderOfTut = onBorderOfTarget;
                onBorderOfTut.X = Math.Max(WindowBounds.Left, Math.Min(WindowBounds.Right, onBorderOfTut.X));
                onBorderOfTut.Y = Math.Max(WindowBounds.Top, Math.Min(WindowBounds.Bottom, onBorderOfTut.Y));

                DrawShadowedLine(onBorderOfTut, onBorderOfTarget, BorderColor);
            }

            base.Draw(a_Device);
        }

        public Tutorial Show(string a_title, string a_msg, float a_MinTimeToShow)
        {
            Tutorial t = new Tutorial();

            t.m_Title = a_title;
            t.m_MinTimeToShow = a_MinTimeToShow;
            SplitMessage(a_msg, t);

            if (m_DeferredTuts.Count == 0 && (GetActiveTut() == null || GetActiveTut().GetAge() >= GetActiveTut().m_MinTimeToShow))
            {
                m_Tutorials.Add(t);
                m_TutIndexToShow = m_Tutorials.Count - 1; 
            }
            else
            {
                m_DeferredTuts.Add(t);
            }
            
            m_bVisible = true;
            m_bKeepTutVisible = true;

            return t;
        }

        public Tutorial Show(string a_title, string a_msg, float a_minTimeToShow, CellWindow a_ToShow)
        {
            Tutorial newT = Show(a_title, a_msg, a_minTimeToShow);
            newT.m_TargetWindow = a_ToShow;
            return newT;
        }

        public Tutorial Show(string a_title, string a_Msg, float a_minTimeToShow, Entity a_ToShow)
        {
            Tutorial newT = Show(a_title, a_Msg, a_minTimeToShow);
            newT.m_TargetEntity = a_ToShow;
            return newT;
        }

        public void SplitMessage(string a_Msg, Tutorial a_T)
        {
            a_T.m_WholeMsg = a_Msg;
            int startIndex = 0;
            int numLines = 1;

            while (true)
            {
                int searchWidth = Math.Min(a_T.m_WholeMsg.Length - startIndex, Width - 2);
                if (searchWidth <= 0)
                    break;

                int endOfLine = a_T.m_WholeMsg.IndexOf('\n', startIndex, searchWidth);
                if (endOfLine == -1 && searchWidth < Width - 2)
                {
                    endOfLine = a_T.m_WholeMsg.Length;
                }
                if (endOfLine == -1)
                {
                    endOfLine = a_T.m_WholeMsg.LastIndexOf(' ', startIndex + searchWidth - 1, searchWidth);
                }

                a_T.m_Lines.Add(a_T.m_WholeMsg.Substring(startIndex, endOfLine - startIndex));
                ++numLines;

                startIndex = endOfLine + 1;
            }
        }

        public void Close()
        {
            m_bVisible = false;
        }

    }
}
